package cn.qu.phizline.connect.channel;

import android.os.Handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.qu.phizline.AndroidPayloadConventer;
import cn.qu.phizline.PhizlineApplication;
import cn.qu.phizline.activity.IntentExtraKeys;
import cn.qu.phizline.common.TypeUtils;
import cn.qu.phizline.connect.WebSocketChannelEvents;
import cn.qu.phizline.connect.handle.Handle;
import cn.qu.phizline.event.common.ResponseEvent;
import cn.qu.phizline.event.init.InitEvent;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;


/**
 * Created by zh on 2017/10/24.
 */
public class WebSocketChannelClient {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketChannelClient.class);
    //    private static final String TAG = WebSocketChannelClient.class.getSimpleName();
    private final Handler handler;
    private WebSocketConnectionState state;
    private WebSocketListener websocketListener = new WebsocketObserver();
    //房间ID
    private String roomId;
    //WS对象
    private WebSocket webSocket;

    private Object closeEventLock = new Object();
    private boolean closeEvent;

    public enum WebSocketConnectionState {
        NEW, //创建对象
        CONNECTED,//已连接到WS服务
        LOGINED,//已登录成功
        REGISTERED,
        CLOSED,
        ERROR
    }

    final private WebSocketChannelEvents events;

    public WebSocketChannelClient(Handler handler, WebSocketChannelEvents events) {
        this.handler = handler;
        this.events = events;
        state = WebSocketConnectionState.NEW;
    }

    public void connect(String connectUrl) {
//        checkIfCalledOnValidThread();
        OkHttpClient okHttpClient = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        Request req = builder.url(connectUrl).build();
        this.webSocket = okHttpClient.newWebSocket(req, websocketListener);

        closeEvent = false;
    }

    public void send(final byte[] bytes) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                webSocket.send(ByteString.of(bytes));
            }
        });
    }

    /*
    public void register(final String roomID) {
//        checkIfCalledOnValidThread();
        this.roomId = roomID;
        if (state != WebSocketConnectionState.CONNECTED) {
            logger.warn("WebSocket register() in state " + state);
            return;
        }
        logger.debug("Registering WebSocket for room " + roomID + ". ClientID: " + clientId);
        JSONObject json = new JSONObject();
        try {
            json.put("cmd", "register");
            json.put("roomid", roomID);
            json.put("clientid", clientId);
            logger.debug("C->WSS: " + json.toString());
            webSocket.send(json.toString());
            state = WebSocketConnectionState.REGISTERED;
        } catch (JSONException e) {
            logger.error("WebSocket register JSON error: " + e.getMessage());
        }
    }
    */

    private class WebsocketObserver extends WebSocketListener implements IntentExtraKeys {

        @Override
        public void onOpen(final WebSocket webSocket, Response response) {
            String token = PhizlineApplication.getToken();

            String deivceId = PhizlineApplication.getDeviceId();
            final InitEvent initEvent = new InitEvent();
            InitEvent.InitBody body = new InitEvent.InitBody();
            body.setDeviceId(deivceId);
            body.setToken(token);
            initEvent.setBody(body);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    //Websocket已连接
                    state = WebSocketConnectionState.CONNECTED;
                    webSocket.send(ByteString.of(new AndroidPayloadConventer().toBytes(initEvent)));
                }
            });

//            logger.debug("WebSocket connection opened to: " + clientId);
//            handler.post(new Runnable() {
//                @Override
//                public void run() {
//                    if (roomId != null) {
//                        register(roomId);
//                    }
//                }
//            });
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            logger.debug("WSS->C text ignore : " + text);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            logger.debug("WSS->C bytes");

            ResponseEvent payload = PhizlineApplication.getProtoTypes().load(bytes.toByteArray());

            ResponseEvent.ResponseBody body = (ResponseEvent.ResponseBody) payload.getBody();

            String key = body.getEventKey();

            Class<? extends Handle> handleType = PhizlineApplication.getProtoTypes().getHandleType(key);

            Handle h = TypeUtils.newInstance(handleType);

            h.handle(events, payload);
        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {

        }

        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
            logger.debug("WebSocket connection closed. Code: " + code + ". Reason: " + reason + ". State: " + state);
            synchronized (closeEventLock) {
                closeEvent = true;
                closeEventLock.notify();
            }
//            handler.post(new Runnable() {
//                @Override
//                public void run() {
//                    if (state != WebSocketConnectionState.CLOSED) {
//                        state = WebSocketConnectionState.CLOSED;
//                        events.onWebSocketClose();
//                    }
//                }
//            });
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {

            System.out.println(t);
        }
    }

    private void checkIfCalledOnValidThread() {
        if (Thread.currentThread() != handler.getLooper().getThread()) {
            throw new IllegalStateException("WebSocket method is not called on valid thread");
        }
    }

}
